package org.ams.common.util;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.ams.common.annotation.Log;
import org.ams.log.entity.OperationLog;
import org.ams.log.service.impl.OperationLogServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 
 * 前置通知（@Before）：在某连接点（join point）之前执行的通知，但这个通知不能阻止连接点前的执行（除非它抛出一个异常）
 * 返回后通知（@AfterReturning）：在某连接点（join point）正常完成后执行的通知：例如，一个方法没有抛出任何异常，正常返回
 * 抛出异常后通知（@AfterThrowing）：方法抛出异常退出时执行的通知
 * 后通知（@After）：当某连接点退出的时候执行的通知（不论是正常返回还是异常退出） 环绕通知（@Around）：包围一个连接点（join
 * point）的通知，如方法调用。这是最强大的一种通知类型，环绕通知可以在方法调用前后完成自定义的行为，
 * 它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行
 * Description: 日志记录，添加、删除、修改方法AOP
 * @author Yannis
 * @date 2016年3月6日 下午11:40:26
 */
@Component
@Aspect
public class LogAspect {

	//本地异常日志记录对象
    private  static  final Logger logger = LoggerFactory.getLogger(LogAspect. class);
	
	@Autowired
	private OperationLogServiceImpl operationLogService;// 操作日志记录Service

	@Pointcut("@annotation(org.ams.common.annotation.Log)")
	public void controllerAspect() {}
	
	/**
	 * 前置通知 用于拦截Controller层记录用户的操作
	 * @param joinPoint 切点
	 */
	@Around("controllerAspect()")
	public Object doController(ProceedingJoinPoint point) {
		Object result = null;
		// 执行方法名
		String methodName = point.getSignature().getName();
		String className = point.getTarget().getClass().getSimpleName();
		OperationLog operationLog = new OperationLog();
		Map<String, Object> map = null;
		String accountName = null;
		Integer accountId = null;
		Long start = 0L;
		Long end = 0L;
		Long time = 0L;
		String ip = null;
		try {
			ip = SecurityUtils.getSubject().getSession().getHost();
			if(ip.equals("0:0:0:0:0:0:0:1")){
				ip = "127.0.0.1";
			}
		} catch (Exception e) {
			ip = "无法获取登录用户Ip";
		}
		try {
			accountId = (Integer) SecurityUtils.getSubject().getSession().getAttribute("accountSessionId");
			// 登录名
			accountName = SecurityUtils.getSubject().getPrincipal().toString();
			if (StringUtils.isBlank(accountName)) {
				accountName = "无法获取登录用户信息！";
			}
		} catch (Exception ee) {
			accountName = "无法获取登录用户信息！";
		}
		// 当前用户
		try {
			map = getControllerMethodDescription(point);
			// 执行方法所消耗的时间
			start = System.currentTimeMillis();
			result = point.proceed();
			end = System.currentTimeMillis();
			time = end - start;
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}
		operationLog.setAccountId(accountId);// 设置管理员id
		operationLog.setAccountName(accountName);
		operationLog.setAccountIp(ip);
		operationLog.setResponseTime(time);// 操作响应时间
		operationLog.setModule((String) map.get("module"));
		operationLog.setMethods((String) map.get("methods"));
		operationLog.setContent((String) map.get("content"));// 操作内容
		operationLog.setOperationTime(new Date());// 操作时间
		operationLogService.save(operationLog);// 添加日志
		
		logger.info("请求方法:" + className + "." + methodName + "();方法描述:" + map + ";请求IP:" + ip);
		return result;
	}
	
	/**
     * 操作异常记录
     *@description
     *@param point
     *@param e
     */
	@AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
	public void doAfterThrowing(JoinPoint point, Throwable e) {
		OperationLog operationLog = new OperationLog();
		Map<String, Object> map = null;
		String accountName = null;
		Integer accountId = null;
		String ip = null;
		try {
			ip = SecurityUtils.getSubject().getSession().getHost();
			if(ip.equals("0:0:0:0:0:0:0:1")){
				ip = "127.0.0.1";
			}
		} catch (Exception exception) {
			ip = "无法获取登录用户Ip";
		}
		try {
			map = getControllerMethodDescription(point);
			accountId = (Integer) SecurityUtils.getSubject().getSession().getAttribute("accountSessionId");
			// 登录名
			accountName = SecurityUtils.getSubject().getPrincipal().toString();
			if (StringUtils.isBlank(accountName)) {
				accountName = "无法获取登录用户信息！";
			}
		} catch (Exception ee) {
			accountName = "无法获取登录用户信息！";
		}
		operationLog.setAccountId(accountId);// 设置管理员id
		operationLog.setAccountName(accountName);
		operationLog.setAccountIp(ip);
		operationLog.setResponseTime(0L);// 操作响应时间
		operationLog.setModule((String) map.get("module"));
		operationLog.setMethods("<font color=\"red\">执行方法异常:-->"+map.get("methods")+"</font>");
		operationLog.setContent("<font color=\"red\">执行方法异常:-->"+e+"</font>");// 操作内容
		operationLog.setOperationTime(new Date());// 操作时间
		operationLogService.save(operationLog);// 添加日志
	}
	
	/**
	 * 获取注解中对方法的描述信息 用于Controller层注解
	 * @param joinPoint 切点
	 * @return 方法描述
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	public Map<String, Object> getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
		Map<String, Object> map = new HashMap<String, Object>();
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					map.put("module", method.getAnnotation(Log.class).module());
					map.put("methods", method.getAnnotation(Log.class).methods());
					String de = method.getAnnotation(Log.class).description();
					if (StringUtils.isBlank(de))
						de = "执行成功!";
					map.put("content", de);
					break;
				}
			}
		}
		return map;
	}

}
